#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>


#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

#define MAXARGC 128
//命令行参数表
int g_argc=0;
char* g_argv[MAXARGC];

//for test
char cwd[1024];
char cwdenv[1024];

//环境变量表
#define MAXENVS 100
int g_envs=0;
char* g_env[MAXENVS];


//重定向
#define REDIR_NONE 0
#define REDIR_INPUT 1
#define REDIR_OUTPUT 2
#define REDIR_APPEND 3

int redir=REDIR_NONE;
std::string filename;

int lastcode=0;

const char* GetUserName()
{
    const char* username=getenv("USER");

    return username==NULL?"None":username;
}

const char* GetHostName()
{
    const char* hostname=getenv("HOSTNAME");
    return hostname==NULL?"None":hostname;
}

const char* GetPwd()
{
   // const char* pwd=getenv("PWD");
   // 导环境变量
    const char* pwd=getcwd(cwd,sizeof(cwd));
    if(pwd!=NULL)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return pwd==NULL?"None":pwd;
}


const char* GetHome()
{
    const char* home=getenv("HOME");
    return home==NULL?"":home;
}

void InitEnv()
{
    extern char** environ;

    memset(g_env,0,sizeof(g_env));
    g_envs=0;

    //导入父进程的环境变量，本来应该从配置文件中来
    for(int i=0;environ[i];i++)
    {
        g_env[i]=(char*)malloc(strlen(environ[i])+1);

        strcpy(g_env[i],environ[i]);
        g_envs++;
    }

    g_env[g_envs++]=(char*)"HAHA=for test";
    g_env[g_envs]=NULL;

    //添加进自己的环境变量表
    for(int i=0;g_env[i];i++)
    {
        putenv(g_env[i]);
    }
}

bool Cd()
{
    if(g_argc==1)
    {
        std::string home=GetHome();
        if(home.empty())
        {
            return true;
        }

        chdir(home.c_str());
    }
    else 
    {
        std::string where=g_argv[1];

        if(where=="-")
        {
            //todo
        }
        else if(where=="~")
        {
            //todo
        }
        else 
        {
            chdir(where.c_str());
        }
    }
    return true;
}


void Echo()
{
    if(g_argc==2)
    {
        std::string opt=g_argv[1];

        if(opt=="$?")
        {
            std::cout<<lastcode<<std::endl;
            lastcode=0;
        }
       else if(opt[0]=='$')
        {
            std::string env_name=opt.substr(1);
            const char* env_value=getenv(env_name.c_str());
            if(env_value)
            {
                std::cout<<env_value<<std::endl;
            }
        }
       else 
       {
           std::cout<<opt<<std::endl;
       }
    }
}

void Export()
{
    if(g_argc==2)
    {
         putenv(g_argv[1]);
         lastcode=0;
    }
    else 
    {
        lastcode=1;
    }
}

std::string GetDirName(const char* pwd)
{
#define SLASH "/"

    std::string dir=pwd;
    if(dir==SLASH)
    {
        return "/"; 
    }
    
    auto pos=dir.rfind(SLASH);

    if(pos==std::string::npos)
    {
        return "BUG?";
    }

    return dir.substr(pos+1);

}


void MakeCommandLine(char cmd_prompt[],int size)
{
    snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),/*GetPwd()*/GetDirName(GetPwd()).c_str());
}

void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}


bool GetCommandLine(char* out,int size)
{
    char* c=fgets(out,size,stdin);
    if(c==NULL)
    {
        return false;
    }

    out[strlen(out)-1]=0;
    if(strlen(out)==0)
    {
        return false;
    }

    return true;
}


bool CommandParse(char* cmd)
{
#define SEP " "

     g_argc=0;

     g_argv[g_argc++]=strtok(cmd,SEP);

     while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));

     g_argc--;
     return g_argc>0?true:false;

}

void PrintArray()
{
    for(int i=0;g_argv[i];i++)
    {
        printf("argv[%d] -> %s\n",i,g_argv[i]);
    }
    printf("%d\n",g_argc);
}

bool CheakAndExecBuiltin()
{
    std::string cmd=g_argv[0];

    if(cmd=="cd")
    {
        Cd();
        return true;
    }
    else if(cmd=="echo")
    {
        Echo();
        return true;
    }
    else if(cmd=="export")
    {
        Export();
        return true;
    }

    return false;
}

int Execute()
{
    pid_t id=fork();

    if(id==0)
    {
        //一定是要让子进程重定向，若父进程重定向则会影响到bash为其打开的0，1，2三个标准文件，而子进程有自己的文件描述符表，不会影响到bash
        int fd=-1;

        if(redir==REDIR_INPUT)
        {
            fd=open(filename.c_str(),O_RDONLY);
            if(fd<0)
            {
                exit(1);
            }

            dup2(fd,0);
            close(fd);
        }
        else if(redir==REDIR_OUTPUT)
        {
            fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd<0)
            {
                exit(2);
            }

            dup2(fd,1);
            close(fd);
        }
        else if(redir==REDIR_APPEND)
        {
            fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd<0)
            {
                exit(2);
            }
            dup2(fd,1);
            close(fd);
        }
        else 
        {

        }
        //进程替换不会影响重定向的结果
        execvp(g_argv[0],g_argv);
        exit(1);
    }

    int status=0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
        lastcode=WEXITSTATUS(status);
    }

    return 0;
}

void Trimspace(char cmd[],int& end)
{
    while(isspace(cmd[end]))
    {
        ++end;
    }
}

void RedirCheck(char cmd[])
{
    redir=REDIR_NONE;
    filename.clear();

    int start=0;
    int end=strlen(cmd)-1;

    while(end>start)
    {
        if(cmd[end]=='<')
        {
            cmd[end++]=0;
            Trimspace(cmd,end);
            filename=cmd+end;
            redir=REDIR_INPUT;
            break;
        }
        else if(cmd[end]=='>')
        {
            if(cmd[end-1]=='>')
            {
                cmd[end-1]=0;
                redir=REDIR_APPEND;
            }
            else 
            {
                redir=REDIR_OUTPUT;
            }
            
             cmd[end++]=0;
             Trimspace(cmd,end);
             filename=cmd+end;
             break;
        }

        else 
        {
            --end;
        }
    }
}

int main()
{
  
    InitEnv();

    while(1)
    {
        
        //1.输出命令行提示符
        PrintCommandPrompt();


        //2.获取用户输入的命令
        char command[COMMAND_SIZE];

        if(!GetCommandLine(command,sizeof(command)))
        {
            continue;
        }


        //3.判断重定向方式
        RedirCheck(command);

       //  printf("echo %s\n",command);
       //4.命令做分割解释
      if(!CommandParse(command))
      {
          continue;
      }
       //PrintArray();
       
       //5.检测并处理内建命令
       if(CheakAndExecBuiltin())
       {
           continue;
       }
       
       //6.执行命令
       Execute();
    }

    return 0;
}
